﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Tetris.Data
{
    struct StructPoint
    {
        public StructPoint(int row, int col)
        {
            _row = row;
            _col = col;
        }

        public int Row
        {
            get
            {
                return _row;
            }

            set
            {
                _row = value;
            }
        }

        public int Col
        {
            get
            {
                return _col;
            }

            set
            {
                _col = value;
            }
        }

        private int _row;
        private int _col;
    }

    class Diamonds
    {
        public int[,] Diamond
        {
            get
            {
                return _diamond;
            }
            set
            {
                _diamond = value;
            }
        }

        public StructPoint PosInMap
        {
            get
            {
                return _posInMap;
            }
            set
            {
                _posInMap = value;
            }
        }

        private int[,] _diamond;
        private StructPoint _posInMap;
    }

    class TetrisData
    {
        public void MoveDown()
        {
            _tetris[_index].PosInMap = new StructPoint(_posInMap.Row++, _posInMap.Col);
        }

        public void MoveLeft()
        {
            _tetris[_index].PosInMap = new StructPoint(_posInMap.Row, _posInMap.Col--);
        }

        public void MoveRight()
        {
            _tetris[_index].PosInMap = new StructPoint(_posInMap.Row, _posInMap.Col++);
        }

        public void Change()
        {
            _index++;
            _index = _index < _maxIndex ? _index : 0;
        }

        public Diamonds NextTetri()
        {
            int tmpIndex = _index+1 < _maxIndex ? _index+1 : 0;
            return _tetris[tmpIndex];
        }

        public Diamonds Tetri
        {
            get
            {
                return _tetris[_index];
            }
        }

        public Color TetriColor
        {
            get
            {
                return _tetriColor;
            }
        }

        public StructPoint PosInMap
        {
            get
            {
                return _posInMap;
            }
        }
        
        protected StructPoint _posInMap;
        protected List<Diamonds> _tetris = new List<Diamonds>();
        protected int _index;
        protected int _maxIndex;
        protected Color _tetriColor = Color.Red;
    }

    class TetrisI : TetrisData
    {
        public TetrisI() : base()
        {
            Diamonds typeOne = new Diamonds();
            typeOne.Diamond = new int[,]
                                {
                                    { 0, 1, 0, 0 },
                                    { 0, 1, 0, 0 },
                                    { 0, 1, 0, 0 },
                                    { 0, 1, 0, 0 }
                                };
            typeOne.PosInMap = new StructPoint(-4, 4);
            Diamonds typeTwo = new Diamonds();
            typeTwo.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 0, 0 },
                                    { 1, 1, 1, 1 },
                                    { 0, 0, 0, 0 }
                                };
            typeTwo.PosInMap = new StructPoint(-4, 3);
            _tetris.Add(typeOne);
            _tetris.Add(typeTwo);
            _maxIndex = 2;
            Random ran = new Random((int)DateTime.Now.Ticks);
            _index = ran.Next(0, _maxIndex);
            _posInMap = _tetris[_index].PosInMap;
            _tetriColor = Color.Red;
        }
    }

    class TetrisJ : TetrisData
    {
        public TetrisJ():base()
        {
            Diamonds typeOne = new Diamonds();
            typeOne.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 0, 2, 0, 0 },
                                    { 0, 2, 0, 0 },
                                    { 2, 2, 0, 0 }
                                };
            typeOne.PosInMap = new StructPoint(-4, 4);
            Diamonds typeTwo = new Diamonds();
            typeTwo.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 0, 0 },
                                    { 2, 0, 0, 0 },
                                    { 2, 2, 2, 0 }
                                };
            typeTwo.PosInMap = new StructPoint(-4, 4);
            Diamonds typeThree = new Diamonds();
            typeThree.Diamond = new int[,]
                                 {
                                    { 0, 0, 0, 0 },
                                    { 2, 2, 0, 0 },
                                    { 2, 0, 0, 0 },
                                    { 2, 0, 0, 0 }
                                };
            typeThree.PosInMap = new StructPoint(-4, 4);
            Diamonds typeFour = new Diamonds();
            typeFour.Diamond = new int[,]
                                 {
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 0, 0 },
                                    { 2, 2, 2, 0 },
                                    { 0, 0, 2, 0 }
                                };
            typeFour.PosInMap = new StructPoint(-4, 4);
            _tetris.Add(typeOne);
            _tetris.Add(typeTwo);
            _tetris.Add(typeThree);
            _tetris.Add(typeFour);

            _maxIndex = 4;
            Random ran = new Random((int)DateTime.Now.Ticks);
            _index = ran.Next(0, _maxIndex);
            _posInMap = _tetris[_index].PosInMap;
            _tetriColor = Color.Blue;
        }
    }

    class TetrisL : TetrisData
    {
        public TetrisL():base()
        {
            Diamonds typeOne = new Diamonds();
            typeOne.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 3, 0, 0, 0 },
                                    { 3, 0, 0, 0 },
                                    { 3, 3, 0, 0 }
                                };
            typeOne.PosInMap = new StructPoint(-4, 4);
            Diamonds typeTwo = new Diamonds();
            typeTwo.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 0, 0 },
                                    { 3, 3, 3, 0 },
                                    { 3, 0, 0, 0 }
                                };
            typeTwo.PosInMap = new StructPoint(-4, 4);
            Diamonds typeThree = new Diamonds();
            typeThree.Diamond = new int[,]
                                 {
                                    { 0, 0, 0, 0 },
                                    { 3, 3, 0, 0 },
                                    { 0, 3, 0, 0 },
                                    { 0, 3, 0, 0 }
                                };
            typeThree.PosInMap = new StructPoint(-4, 4);
            Diamonds typeFour = new Diamonds();
            typeFour.Diamond = new int[,]
                                 {
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 3, 0 },
                                    { 3, 3, 3, 0 }
                                };
            typeFour.PosInMap = new StructPoint(-4, 4);
            _tetris.Add(typeOne);
            _tetris.Add(typeTwo);
            _tetris.Add(typeThree);
            _tetris.Add(typeFour);

            _maxIndex = 4;
            Random ran = new Random((int)DateTime.Now.Ticks);
            _index = ran.Next(0, _maxIndex);
            _posInMap = _tetris[_index].PosInMap;
            _tetriColor = Color.Green;
        }
    }

    class TetrisO : TetrisData
    {
        public TetrisO() : base()
        {
            Diamonds typeOne = new Diamonds();
            typeOne.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 0, 0 },
                                    { 4, 4, 0, 0 },
                                    { 4, 4, 0, 0 }
                                };
            typeOne.PosInMap = new StructPoint(-4, 4);
            _tetris.Add(typeOne);

            _maxIndex = 1;
            Random ran = new Random((int)DateTime.Now.Ticks);
            _index = ran.Next(0, _maxIndex);
            _posInMap = _tetris[_index].PosInMap;
            _tetriColor = Color.Gold;
        }
    }

    class TetrisS : TetrisData
    {
        public TetrisS()
        {
            Diamonds typeOne = new Diamonds();
            typeOne.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 0, 0 },
                                    { 0, 5, 5, 0 },
                                    { 5, 5, 0, 0 }
                                };
            typeOne.PosInMap = new StructPoint(-4, 4);
            Diamonds typeTwo = new Diamonds();
            typeTwo.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 5, 0, 0, 0 },
                                    { 5, 5, 0, 0 },
                                    { 0, 5, 0, 0 }
                                };
            typeTwo.PosInMap = new StructPoint(-4, 4);
            _tetris.Add(typeOne);
            _tetris.Add(typeTwo);

            _maxIndex = 2;
            Random ran = new Random((int)DateTime.Now.Ticks);
            _index = ran.Next(0, _maxIndex);
            _posInMap = _tetris[_index].PosInMap;
            _tetriColor = Color.Orange;
        }
    }

    class TetrisZ : TetrisData
    {
        public TetrisZ()
        {
            Diamonds typeOne = new Diamonds();
            typeOne.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 0, 0 },
                                    { 6, 6, 0, 0 },
                                    { 0, 6, 6, 0 }
                                };
            typeOne.PosInMap = new StructPoint(-4, 4);
            Diamonds typeTwo = new Diamonds();
            typeTwo.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 0, 6, 0, 0 },
                                    { 6, 6, 0, 0 },
                                    { 6, 0, 0, 0 }
                                };
            typeTwo.PosInMap = new StructPoint(-4, 4);
            _tetris.Add(typeOne);
            _tetris.Add(typeTwo);

            _maxIndex = 2;
            Random ran = new Random((int)DateTime.Now.Ticks);
            _index = ran.Next(0, _maxIndex);
            _posInMap = _tetris[_index].PosInMap;
            _tetriColor = Color.Pink;
        }
    }

    class TetrisT : TetrisData
    {
        public TetrisT()
        {
            Diamonds typeOne = new Diamonds();
            typeOne.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 0, 0 },
                                    { 0, 7, 0, 0 },
                                    { 7, 7, 7, 0 }
                                };
            typeOne.PosInMap = new StructPoint(-4, 4);
            Diamonds typeTwo = new Diamonds();
            typeTwo.Diamond = new int[,]
                                {
                                    { 0, 0, 0, 0 },
                                    { 0, 7, 0, 0 },
                                    { 0, 7, 7, 0 },
                                    { 0, 7, 0, 0 }
                                };
            typeTwo.PosInMap = new StructPoint(-4, 4);
            Diamonds typeThree = new Diamonds();
            typeThree.Diamond = new int[,]
                                 {
                                    { 0, 0, 0, 0 },
                                    { 0, 0, 0, 0 },
                                    { 7, 7, 7, 0 },
                                    { 0, 7, 0, 0 }
                                };
            typeThree.PosInMap = new StructPoint(-4, 4);
            Diamonds typeFour = new Diamonds();
            typeFour.Diamond = new int[,]
                                 {
                                    { 0, 0, 0, 0 },
                                    { 0, 7, 0, 0 },
                                    { 7, 7, 0, 0 },
                                    { 0, 7, 0, 0 }
                                };
            typeFour.PosInMap = new StructPoint(-4, 4);
            _tetris.Add(typeOne);
            _tetris.Add(typeTwo);
            _tetris.Add(typeThree);
            _tetris.Add(typeFour);

            _maxIndex = 4;
            Random ran = new Random((int)DateTime.Now.Ticks);
            _index = ran.Next(0, _maxIndex);
            _posInMap = _tetris[_index].PosInMap;
            _tetriColor = Color.Yellow;
        }
    }

    class Map
    {
        public bool StopToMoveDown(TetrisData tetri)
        {
            int tetriRow = tetri.Tetri.PosInMap.Row;
            int tetriCol = tetri.Tetri.PosInMap.Col;
            
            for (int row = 3; row >= 0; row--)
            {
                for (int col = 0; col < 4; col++)
                {
                    if (tetri.Tetri.Diamond[row, col] > 0)
                    {
                        if (tetri.PosInMap.Row + row < 0)
                        {
                            return false;
                        }
                        //最底行
                        if (tetri.PosInMap.Row + row >= _mapRows-1)
                        {
                            return true;
                        }
                        //下方存在方块
                        if (tetri.PosInMap.Col + col < 0 || tetri.PosInMap.Col + col >= _mapCols -1)
                        {
                            return false;
                        }
                        if (_map[tetri.PosInMap.Row + row + 1, tetri.PosInMap.Col + col] > 0)
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        public bool StopToMoveLeft(TetrisData tetri)
        {
            int tetriRow = tetri.Tetri.PosInMap.Row;
            int tetriCol = tetri.Tetri.PosInMap.Col;
            
            for (int col = 0; col < 4; col++)
            {
                for (int row = 3; row >= 0; row--)
                {
                    if (tetri.Tetri.Diamond[row, col] > 0)
                    {
                        if (tetri.PosInMap.Row + row < 0)
                        {
                            return false;
                        }
                        //最左列
                        if (tetri.PosInMap.Col + col <= 0)
                        {
                            return true;
                        }
                        //左方存在方块
                        if (tetri.PosInMap.Row + row > _mapRows - 1)
                        {
                            return false;
                        }
                        if (_map[tetri.PosInMap.Row + row, tetri.PosInMap.Col + col] > 0)
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        public bool StopToMoveRight(TetrisData tetri)
        {
            int tetriRow = tetri.Tetri.PosInMap.Row;
            int tetriCol = tetri.Tetri.PosInMap.Col;
            
            #region[判断是否需要停止]
            for (int col = 3; col >= 0; col--)
            {
                for (int row = 3; row >= 0; row--)
                {
                    if (tetri.Tetri.Diamond[row, col] > 0)
                    {
                        if (tetri.PosInMap.Row + row < 0)
                        {
                            return false;
                        }
                        //最右列
                        if (tetri.PosInMap.Col + col >= _mapCols-1)
                        {
                            return true;
                        }
                        //右方存在方块
                        if (tetri.PosInMap.Row + row > _mapRows - 1)
                        {
                            return false;
                        }
                        if (_map[tetri.PosInMap.Row + row, tetri.PosInMap.Col + col] > 0)
                        {
                            return true;
                        }
                    }
                }
            }
            #endregion
            return false;
        }

        public bool CheckToChange(TetrisData tetri)
        {
            bool ret = true;
            Diamonds nextTetri = tetri.NextTetri();
            for (int row = 0; row < 4; row++)
            {
                for (int col = 0; col < 4; col++)
                {
                    if (tetri.PosInMap.Row + row < 0)
                    {
                        ret = true;
                        break;
                    }
                    if (tetri.PosInMap.Row + row > _mapRows-1)
                    {
                        ret = false;
                        break;
                    }
                    if (tetri.PosInMap.Col + col < 0)
                    {
                        ret = false;
                        break;
                    }
                    if (tetri.PosInMap.Col + col > _mapCols - 1)
                    {
                        ret = false;
                        break;
                    }
                    if ((nextTetri.Diamond[row, col] > 0) && (_map[tetri.PosInMap.Row + row, tetri.PosInMap.Col + col] > 0))
                    {
                        ret = false;
                        break;
                    }
                }
                if (ret == false)
                {
                    break;
                }
            }
            return ret;
        }

        public bool SetMapData(TetrisData tetri)
        {
            int row = tetri.Tetri.PosInMap.Row;
            int col = tetri.Tetri.PosInMap.Col;
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (row + i < 0)
                    {
                        return false;
                    }
                    if (tetri.Tetri.Diamond[i, j] > 0)
                    {
                        //if ((row + i >= _mapRows) || (col + j >= _mapCols) || (col + j < 0))
                        //{
                        //    return true;
                        //}
                        _map[row + i, col + j] += tetri.Tetri.Diamond[i, j]; 
                    }
                }
            }
            return true;
        }

        public void ClearMap()
        {
            _map = new int[,]
            {
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            }; 
        }

        /// <summary>
        /// 判断该行是否全占用
        /// </summary>
        /// <param name="lineNum">行号</param>
        /// <returns>true-满行，false-非满行</returns>
        public bool LineIsFull(int lineNum)
        {
            bool ret = true;
            for (int i = 0; i < _mapCols; i++)
            {
                if (_map[lineNum, i] == 0)
                {
                    ret = false;
                    break;
                }
            }

            return ret;
        }

        /// <summary>
        /// 判断该行是否为空
        /// </summary>
        /// <param name="lineNum">行号</param>
        /// <returns>true-空行，false-非空行</returns>
        public bool LineIsEmpty(int lineNum)
        {
            bool ret = true;
            for (int i = 0; i < _mapCols; i++)
            {
                if (_map[lineNum, i] != 0)
                {
                    ret = false;
                    break;
                }
            }

            return ret;
        }

        public void ClearLine(int lineNum)
        {
            for (int i = 0; i < _mapCols; i++)
            {
                _map[lineNum, i] = 0;
            }
        }

        public void UpdateMap()
        {
            int[,] tmpMap = new int[_mapRows, _mapCols];
            int tmpRow = 19;
            for (int row = _mapRows - 1; row > 0; row--)
            {
                if (!LineIsEmpty(row))
                {
                    for (int col = 0; col < _mapCols; col++)
                    {
                        tmpMap[tmpRow, col] = _map[row, col];
                    }
                    tmpRow--;
                }
            }
            _map = tmpMap;
        }

        public int[,] MapStatus
        {
            get
            {
                return _map;
            }
        }

        public int MapRows
        {
            get
            {
                return _mapRows;
            }

            set
            {
                _mapRows = value;
            }
        }

        public int MapCols
        {
            get
            {
                return _mapCols;
            }

            set
            {
                _mapCols = value;
            }
        }

        public int MapX
        {
            get
            {
                return _mapX;
            }

            set
            {
                _mapX = value;
            }
        }

        public int MapY
        {
            get
            {
                return _mapY;
            }

            set
            {
                _mapY = value;
            }
        }

        public int MapWidth
        {
            get
            {
                return _mapWidth;
            }

            set
            {
                _mapWidth = value;
            }
        }

        public int MapHeight
        {
            get
            {
                return _mapHeight;
            }

            set
            {
                _mapHeight = value;
            }
        }

        public int SizeOfDiamonds
        {
            get
            {
                return sizeOfDiamonds;
            }

            set
            {
                sizeOfDiamonds = value;
            }
        }


        private int[,] _map =
        {
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        };
        private int _mapRows = 20;
        private int _mapCols = 10;
        private int _mapX;
        private int _mapY;
        private int _mapWidth;
        private int _mapHeight;
        //组成方块的大小
        private int sizeOfDiamonds;
    }
}
